﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

using GroupChallenge.Helpers;
using GroupChallenge.Web;
using GroupChallenge.Web.Services;

namespace GroupChallenge
{
    public partial class HomeView : Page, INotifyPropertyChanged
    {
        #region Private Members

        // Manual reset event in the running state.
        private System.Threading.ManualResetEvent resetEvent = new System.Threading.ManualResetEvent(true);

        private bool _GameOver = true;
        private bool _WaitOnPolling = false;

        #endregion                              // Private Members

        #region Properties

        private GroupChallengeSvc.GroupChallengeClient _Client = null;
        public GroupChallengeSvc.GroupChallengeClient Client
        {
            get
            {
                if (null != _Client)
                {
                    if (_Client.State != System.ServiceModel.CommunicationState.Created &&
                        _Client.State != System.ServiceModel.CommunicationState.Opened)
                    {
                        _Client.Abort();
                        _Client = null;
                    }
                }

                if (null == _Client)
                {
                    string endpointConfigurationName = "BasicHttpBinding_IGroupChallenge";
                    string endpointUrl = string.Format("{0}/GroupChallenge/GroupChallenge.svc", Utils.GetHostUrl(false));

                    EndpointAddress endpointAddress = new EndpointAddress(endpointUrl);

                    _Client = new GroupChallengeSvc.GroupChallengeClient(endpointConfigurationName, endpointAddress);

                    // Setup the game polling callback once.
                    _Client.GetCurrentGameStateCompleted += new EventHandler<GroupChallengeSvc.GetCurrentGameStateCompletedEventArgs>(CompletedGetCurrentGameState);
                }

                return _Client;
            }
        }

        #endregion                              // Properties

        #region Initialization

        /// <summary>
        /// Creates a new <see cref="HomeView"/> instance.
        /// </summary>
        public HomeView()
        {
            InitializeComponent();

            StartServerUpdates();

            this.Title = ApplicationStrings.HomeViewTitle;
        }

        #endregion                              // Initialization

        #region UI Events

        /// <summary>
        /// Executes when the user navigates to this page.
        /// </summary>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            LayoutRoot.DataContext = MainApp.CurrentGame;
        }

        private void RestartGameButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            RestartGame();
        }

        private void NextQuestion_Click(object sender, RoutedEventArgs e)
        {
            PlayNextQuestion();
        }

        private void NextQuestionSoundTestButton_Click(object sender, RoutedEventArgs e)
        {
            NextQuestionMediaElement.Stop();
            NextQuestionMediaElement.Play();
        }

        private void QuestionWonSoundTestButton_Click(object sender, RoutedEventArgs e)
        {
            PlayQuestionWon();
        }

        private void PlayQuestionWon()
        {
            QuestionWonMediaElement.Stop();
            QuestionWonMediaElement.Play();
        }

        private void PlayNextQuestion()
        {
            NextQuestionMediaElement.Stop();

            // Get to the next question.
            if (MainApp.CurrentGame.NextQuestion())
                SetCurrentQuestion(MainApp.CurrentGame.CurrentQuestion.QuestionId);
            else
                PlayGameOver();
        }

        private void GameOverButton_Click(object sender, RoutedEventArgs e)
        {
            if (MessageBoxResult.OK == MessageBox.Show("End the game?", "Confirm Game Over", MessageBoxButton.OKCancel))
            {
                PlayGameOver();
            }
        }

        private void PlayGameOver()
        {
            StopGame();
        }

        private void SetCurrentQuestionButton_Click(object sender, RoutedEventArgs e)
        {
            SetCurrentQuestion(sender);
        }

        #endregion                              // UI Events

        #region NotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(String propertyName)
        {
            if (null != PropertyChanged)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion                              // NotifyPropertyChanged

        #region Private Methods

        private void SetCurrentQuestion(object sender)
        {
            Button clickedButton = sender as Button;
            if (null != clickedButton &&
                clickedButton.Content is int)
            {
                int questionId = (int)clickedButton.Content;

                SetCurrentQuestion(questionId);
            }
        }

        private void RestartGame()
        {
            if (MessageBoxResult.OK == MessageBox.Show("Restart the game?", "Confirm Game Restart", MessageBoxButton.OKCancel))
            {
                _GameOver = false;

                try
                {
                    GroupChallengeDomainContext groupChallengeDomainContext = new GroupChallengeDomainContext();

                    var query = groupChallengeDomainContext.ResetGameQuery();
                    groupChallengeDomainContext.Load(query, CompletedRestartGame, true);
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show("Unable to restart the game.\r\n\r\nReason: " + ex.Message, "Restart Game Error", MessageBoxButton.OK);
                }
            }
        }

        private void CompletedRestartGame(LoadOperation loadOperation)
        {
            if (loadOperation.HasError)
            {
                loadOperation.MarkErrorAsHandled();

                MessageBox.Show("Unable to restart the game.\r\n\r\nReason: " + loadOperation.Error, "Restart Game Error", MessageBoxButton.OK);
            }
            else
            {
                if (null != loadOperation.Entities)
                {
                    FriendlyResult friendlyResult = loadOperation.Entities.FirstOrDefault() as FriendlyResult;
                    if (null != friendlyResult)
                    {
                        if (friendlyResult.ResultId == 0)         // Success
                        {
                            MessageBox.Show(friendlyResult.FriendlyText, "Success", MessageBoxButton.OK);

                            RefreshQuestionList();
                        }
                        else
                        {
                            MessageBox.Show(friendlyResult.FriendlyText, "Failure", MessageBoxButton.OK);
                        }
                    }
                }
            }
        }

        private void RefreshQuestionList()
        {
            GroupChallengeDomainContext groupChallengeDomainContext = new GroupChallengeDomainContext();

            var query = groupChallengeDomainContext.GetQuestionsQuery();

            MainApp.CurrentGame.CurrentQuestionIndex = -1;

            groupChallengeDomainContext.Load(query, CompletedListLoad, true);
        }

        private void CompletedListLoad(LoadOperation loadOperation)
        {
            if (loadOperation.HasError)
            {
                loadOperation.MarkErrorAsHandled();

                MessageBox.Show("Unable to retrieve the question list.\r\n\r\nReason: " + loadOperation.Error.Message, "List Load Error", MessageBoxButton.OK);
            }
            else
            {
                MainApp.CurrentGame.RandomizedQuestions = RandomizeQuestions(loadOperation.AllEntities);

                GameQuestionLineupList.ItemsSource = MainApp.CurrentGame.RandomizedQuestions;
            }
        }

        private ObservableCollection<Question> RandomizeQuestions(IEnumerable<Entity> iEnumerable)
        {
            ObservableCollection<Question> ret = null;

            if (null != iEnumerable &&
                iEnumerable.Count() > 0)
            {
                ret = new ObservableCollection<Question>();
                List<Question> questions = iEnumerable.Cast<Question>().ToList();

                Random r = new Random();
                int randomIndex = 0;

                while (questions.Count > 0)
                {
                    randomIndex = r.Next(0, questions.Count - 1);   // New Random Index in remainder.
                    ret.Add(questions[randomIndex]);            // Add to Return list.
                    questions.RemoveAt(randomIndex);            // Remove current item.
                }
            }

            return ret;
        }

        private void SetCurrentQuestion(int questionId)
        {
            if (questionId == 0)
            {
                MessageBox.Show("Invalid question id.  Unable to set the current question.", "Set Current Questions Error", MessageBoxButton.OK);
            }
            else
            {
                try
                {
                    GroupChallengeDomainContext groupChallengeDomainContext = new GroupChallengeDomainContext();

                    WinnerGrid.Visibility = Visibility.Collapsed;

                    NextQuestionMediaElement.Stop();
                    NextQuestionMediaElement.Play();

                    var query = groupChallengeDomainContext.SetCurrentQuestionQuery(questionId);
                    groupChallengeDomainContext.Load(query, CompletedSetCurrentQuestion, true);
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show("Unable to set the current question.\r\n\r\nReason: " + ex.Message, "Set Current Questions Error", MessageBoxButton.OK);
                }
            }
        }

        private void CompletedSetCurrentQuestion(LoadOperation loadOperation)
        {
            if (loadOperation.HasError)
            {
                loadOperation.MarkErrorAsHandled();

                MessageBox.Show("Unable to set the current question.\r\n\r\nReason: " + loadOperation.Error.Message, "Set Current Questions Error", MessageBoxButton.OK);
            }
            else
            {
                if (null != loadOperation.Entities)
                {
                    FriendlyResult friendlyResult = loadOperation.Entities.FirstOrDefault() as FriendlyResult;
                    if (null != friendlyResult)
                    {
                        // Show the current question now.
                        MainApp.CurrentGame.CurrentQuestionId = friendlyResult.ResultId;
                    }
                }
            }
        }

        private void StopGame()
        {
            MainApp.CurrentGame.CurrentQuestionIndex = -1;

            _GameOver = true;

            try
            {
                GroupChallengeDomainContext groupChallengeDomainContext = new GroupChallengeDomainContext();

                NextQuestionMediaElement.Stop();
                NextQuestionMediaElement.Play();

                var query = groupChallengeDomainContext.StopGameQuery();
                groupChallengeDomainContext.Load(query, CompletedStopGame, true);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Unable to stop the game.\r\n\r\nReason: " + ex.Message, "Stop Game Error", MessageBoxButton.OK);
            }
        }

        private void CompletedStopGame(LoadOperation loadOperation)
        {
            if (loadOperation.HasError)
            {
                loadOperation.MarkErrorAsHandled();

                MessageBox.Show("Unable to stop the game.\r\n\r\nReason: " + loadOperation.Error.Message, "Stop Game Error", MessageBoxButton.OK);
            }
            else
            {
                if (null != loadOperation.Entities)
                {
                    FriendlyResult friendlyResult = loadOperation.Entities.FirstOrDefault() as FriendlyResult;
                    if (null != friendlyResult)
                    {
                        MainApp.CurrentGame.WinnerMessage = friendlyResult.FriendlyText;
                    }

                    ShowGameWinner();
                }
            }
        }

        private void StartServerUpdates()
        {
            // Start background thread
            System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));

            t.Name = "WorkerThread";
            t.IsBackground = true;
            t.Start();
        }

        // Background thread method.
        private void ThreadProc()
        {
            while (true)
            {
                // Block until in the running state.
                resetEvent.WaitOne();

                PollServerForUpdate();

                // Still wait for a delay.
                System.Threading.Thread.Sleep(1500);
            }
        }

        private void PollServerForUpdate()
        {
            // Still polling.
            if (_WaitOnPolling)
                return;

            _WaitOnPolling = true;

            Dispatcher.BeginInvoke(() =>
            {
                UpdateGameStats();
            });

            _WaitOnPolling = false;
        }

        private void UpdateGameStats()
        {
            Client.GetCurrentGameStateAsync(MainApp.CurrentGame.LastChangeTime);
        }

        private void CompletedGetCurrentGameState(object sender, GroupChallengeSvc.GetCurrentGameStateCompletedEventArgs e)
        {
            if (null == e.Error)
            {
                if (null != e.currentGame)
                {
                    MainApp.CurrentGame.HighScore = e.currentGame.HighScore;
                    MainApp.CurrentGame.WonByPlayerNickName = e.currentGame.HighScorePlayerNickName;
                }

                // Change occurred.  Set the game and question.
                if (MainApp.CurrentGame.LastChangeTime != e.lastChangeTime)
                {
                    MainApp.CurrentGame.LastChangeTime = e.lastChangeTime;

                    SetWonByPlayer(e.currentQuestion);
                }
            }
            else
            {
                MessageBox.Show("ERROR: " + e.Error.Message);
            }
        }

        private void SetWonByPlayer(GroupChallengeSvc.Question currentQuestion)
        {
            if (!_GameOver &&
                (null == currentQuestion ||
                currentQuestion.WonByPlayerId == 0))
            {
                WinnerGrid.Visibility = Visibility.Collapsed;

                MainApp.CurrentGame.WonByPlayerId = 0;
                MainApp.CurrentGame.WonByPlayerNickName = string.Empty;

                MainApp.CurrentGame.WinnerMessage = string.Empty;

            }
            else if (!_GameOver)
            {
                WinnerGrid.Visibility = Visibility.Visible;

                MainApp.CurrentGame.WonByPlayerId = currentQuestion.WonByPlayerId;
                MainApp.CurrentGame.WonByPlayerNickName = currentQuestion.WonByPlayerNickName;

                MainApp.CurrentGame.WinnerMessage = string.Format("Answered by {0} ({1})",
                    currentQuestion.WonByPlayerNickName, currentQuestion.WonByPlayerId);


                PlayQuestionWon();
            }
            else if (_GameOver &&
                !string.IsNullOrEmpty(MainApp.CurrentGame.WinnerMessage))
            {
                WinnerGrid.Visibility = Visibility.Visible;

                ShowGameWinner();
            }
        }

        private void ShowGameWinner()
        {
            MainApp.CurrentGame.WonByPlayerId = MainApp.CurrentGame.WonByPlayerId;

            ClapMediaElement.Stop();
            ClapMediaElement.Play();
        }

        #endregion                              // Private Methods
    }
}